#include <stdio.h>
#include <string.h>
#include "MEM.h"
#include "DBG.h"
#include "wuji.h"

static WJ_Interpreter *st_current_interpreter;

WJ_Interpreter *
wj_get_current_interpreter(void)
{
    return st_current_interpreter;
}

void
wj_set_current_interpreter(WJ_Interpreter *inter)
{
    st_current_interpreter = inter;
}

FunctionDefinition *
wj_search_function(char *name)
{
    FunctionDefinition *pos;
    WJ_Interpreter *inter;

    inter = wj_get_current_interpreter();
    for (pos = inter->function_list; pos; pos = pos->next) {
        if (!strcmp(pos->name, name))
            break;
    }
    return pos;
}

FunctionDefinition *
wj_search_function_in_compile(char *name)
{
	/* WJ_Interpreter *inter; */
	/* inter = wj_get_current_interpreter(); */
	return wj_search_function(name);
}

void *
wj_malloc(size_t size)
{
    void *p;
    WJ_Interpreter *inter;

    inter = wj_get_current_interpreter();
    p = MEM_storage_malloc(inter->interpreter_storage, size);

    return p;
}

void *
wj_execute_malloc(WJ_Interpreter *inter, size_t size)
{
    void *p;

    p = MEM_storage_malloc(inter->execute_storage, size);

    return p;
}

WJ_Value *
WJ_search_local_variable(WJ_LocalEnvironment *env, char *identifier)
{
	WJ_Value	*value;
	WJ_Object	*sc;

	if (!env) {
		return NULL;
	}

	DBG_assert(env->variable->type == SCOPE_CHAIN_OBJECT, 
			("type.. %d\n", env->variable->type));

	for (sc = env->variable; sc; sc = sc->u.scope_chain.next) {
		DBG_assert(sc->type == SCOPE_CHAIN_OBJECT, 
				("sc->type..%d\n", sc->type));
		value = WJ_search_assoc_member(sc->u.scope_chain.frame, identifier);
		if (value) {
			break;
		}
	}

	return value;
}

WJ_Value *
WJ_search_local_variable_w(WJ_LocalEnvironment *env, char *identifier, WJ_Boolean *is_final)
{
	WJ_Value *value;
	WJ_Object	*sc;

	if (!env) {
		return NULL;
	}

	DBG_assert(env->variable->type == SCOPE_CHAIN_OBJECT, 
			("type.. %d\n", env->variable->type));

	for (sc = env->variable; sc; sc = sc->u.scope_chain.next) {
		DBG_assert(sc->type == SCOPE_CHAIN_OBJECT, 
				("sc->type..%d\n", sc->type));
		value = WJ_search_assoc_member_w(sc->u.scope_chain.frame, identifier, is_final);
		if (value) {
			break;
		}
	}

	return value;
}

WJ_Value *
WJ_search_global_variable(WJ_Interpreter *inter, char *identifier)
{
	Variable *pos;

    for (pos = inter->variable; pos; pos = pos->next) {
        if (!strcmp(pos->name, identifier))
			break;
    }
	if (pos == NULL) {
		return NULL;
	} else {
		return &pos->value;
	}
}

Variable *
wj_search_global_variable(WJ_Interpreter *inter, char *identifier)
{
    Variable    *pos;

    for (pos = inter->variable; pos; pos = pos->next) {
        if (!strcmp(pos->name, identifier))
            return pos;
    }

    return NULL;
}

WJ_Value *
WJ_search_global_variable_w(WJ_Interpreter *inter, char *identifier,
							WJ_Boolean *is_final)
{
    Variable    *pos;

    for (pos = inter->variable; pos; pos = pos->next) {
        if (!strcmp(pos->name, identifier))
			break;
    }
	if (pos == NULL) {
		return NULL;
	} else {
		*is_final = pos->is_final;
		return &pos->value;
	}
}

WJ_Value *
WJ_add_local_variable(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
					char *identifier, WJ_Value *value, WJ_Boolean is_final)
{
	WJ_Value *ret;

	DBG_assert(env->variable->type == SCOPE_CHAIN_OBJECT, 
			("type.. %d\n", env->variable->type));

	ret = WJ_add_assoc_member(inter, env->variable->u.scope_chain.frame,
							identifier, value, is_final);

	return ret;
}


WJ_Value *
WJ_add_global_variable(WJ_Interpreter *inter, char *identifier,
					WJ_Value *value, WJ_Boolean is_final)
{
	Variable	*new_variable;

	new_variable = wj_execute_malloc(inter, sizeof(Variable));
	new_variable->is_final = is_final;
	new_variable->name = wj_execute_malloc(inter, strlen(identifier) + 1);
	strcpy(new_variable->name, identifier);
	new_variable->next = inter->variable;
	inter->variable = new_variable;
	new_variable->value = *value;

	return &new_variable->value;
}

char *
wj_get_operator_string(ExpressionType type)
{
    char        *str;

    switch (type) {
    case BOOLEAN_EXPRESSION:    /* FALLTHRU */
    case INT_EXPRESSION:        /* FALLTHRU */
    case DOUBLE_EXPRESSION:     /* FALLTHRU */
    case STRING_EXPRESSION:     /* FALLTHRU */
	case REGEXP_EXPRESSION:
    case IDENTIFIER_EXPRESSION:
        DBG_panic(("bad expression type..%d\n", type));
        break;
	case COMMA_EXPRESSION:
		str = ",";
		break;
    case ASSIGN_EXPRESSION:
        str = "=";
        break;
    case ADD_EXPRESSION:
        str = "+";
        break;
    case SUB_EXPRESSION:
        str = "-";
        break;
    case MUL_EXPRESSION:
        str = "*";
        break;
    case DIV_EXPRESSION:
        str = "/";
        break;
    case MOD_EXPRESSION:
        str = "%";
        break;
    case EQ_EXPRESSION:
        str = "==";
        break;
    case NE_EXPRESSION:
        str = "!=";
        break;
    case GT_EXPRESSION:
        str = "<";
        break;
    case GE_EXPRESSION:
        str = "<=";
        break;
    case LT_EXPRESSION:
        str = ">";
        break;
    case LE_EXPRESSION:
        str = ">=";
        break;
    case LOGICAL_AND_EXPRESSION:
        str = "&&";
        break;
    case LOGICAL_OR_EXPRESSION:
        str = "||";
        break;
    case MINUS_EXPRESSION:
        str = "-";
        break;
	case LOGICAL_NOT_EXPRESSION:
		str = "!";
		break;
    case FUNCTION_CALL_EXPRESSION:  /* FALLTHRU */
	case MEMBER_EXPRESSION:
    case METHOD_CALL_EXPRESSION:  /* FALLTHRU */
    case NULL_EXPRESSION:  /* FALLTHRU */
    case ARRAY_EXPRESSION:  /* FALLTHRU */
    case INDEX_EXPRESSION:  /* FALLTHRU */
    case INCREMENT_EXPRESSION:  /* FALLTHRU */
    case DECREMENT_EXPRESSION:  /* FALLTHRU */
	case CLOSURE_EXPRESSION:
    case EXPRESSION_TYPE_COUNT_PLUS_1:
    default:
        DBG_panic(("bad expression type..%d\n", type));
    }

    return str;
}

char *
WJ_get_type_name(WJ_ValueType type)
{
	switch(type) {
		case WJ_BOOLEAN_VALUE:
			return "boolean";
		case WJ_INT_VALUE:
			return "int";
		case WJ_DOUBLE_VALUE:
			return "double";
		case WJ_STRING_VALUE:
			return "string";
		case WJ_NATIVE_POINTER_VALUE:
			return "native pointer";
		case WJ_NULL_VALUE:
			return "null";
		case WJ_ARRAY_VALUE:
			return "array";
		case WJ_ASSOC_VALUE:
			return "object";
		case WJ_CLOSURE_VALUE:
			return "closure";
		case WJ_FAKE_METHOD_VALUE:
			return "method";
		case WJ_SCOPE_CHAIN_VALUE:
			return "scope chain";
		default:
			DBG_panic(("bad type.. %d\n", type));
	}
	return NULL;
}

char *
WJ_get_object_type_name(WJ_Object *obj)
{
	switch (obj->type) {
		case ARRAY_OBJECT:
			return "array";
		case STRING_OBJECT:
			return "string";
		case ASSOC_OBJECT:
			return "object";
		case SCOPE_CHAIN_OBJECT:
			return "scope chain";
		case NATIVE_POINTER_OBJECT:
			return "native pointer";
		case OBJECT_TYPE_COUNT_PLUS_1:
		default:
			DBG_assert(0, ("bad object type.. %d\n", obj->type));
	}
	return NULL;
}

void
wj_vstr_clear(VString *v)
{
    v->string = NULL;
}

static int
my_strlen(char *str)
{
    if (str == NULL) {
        return 0;
    }
    return strlen(str);
}

void
wj_vstr_append_string(VString *v, char *str)
{
    int new_size;
    int old_len;

    old_len = my_strlen(v->string);
    new_size = old_len + strlen(str)  + 1;
    v->string = MEM_realloc(v->string, new_size);
    strcpy(&v->string[old_len], str);
}

void
wj_vstr_append_character(VString *v, int ch)
{
    int current_len;
    current_len = my_strlen(v->string);
    v->string = MEM_realloc(v->string, current_len + 2);
    v->string[current_len] = ch;
    v->string[current_len+1] = '\0';
}

char *
WJ_value_to_string(WJ_Value *value)
{
    VString     vstr;
    char        buf[LINE_BUF_SIZE];
    int         i;

    wj_vstr_clear(&vstr);

    switch (value->type) {
    case WJ_BOOLEAN_VALUE:
        if (value->u.boolean_value) {
            wj_vstr_append_string(&vstr, "true");
        } else {
            wj_vstr_append_string(&vstr, "false");
        }
        break;
    case WJ_INT_VALUE:
        sprintf(buf, "%d", value->u.int_value);
        wj_vstr_append_string(&vstr, buf);
        break;
    case WJ_DOUBLE_VALUE:
        sprintf(buf, "%f", value->u.double_value);
        wj_vstr_append_string(&vstr, buf);
        break;
    case WJ_STRING_VALUE:
        wj_vstr_append_string(&vstr, value->u.object->u.string.string);
        break;
    case WJ_NATIVE_POINTER_VALUE:
        sprintf(buf, "(%s:%p)",
				value->u.object->u.native_pointer.info->name,
				value->u.object->u.native_pointer.pointer);
        wj_vstr_append_string(&vstr, buf);
        break;
    case WJ_NULL_VALUE:
        wj_vstr_append_string(&vstr, "null");
        break;
    case WJ_ARRAY_VALUE:
        wj_vstr_append_string(&vstr, "(");
        for (i = 0; i < value->u.object->u.array.size; i++) {
            char *new_str;
            if (i > 0) {
                wj_vstr_append_string(&vstr, ", ");
            }
            new_str = WJ_value_to_string(&value->u.object->u.array.array[i]);
            wj_vstr_append_string(&vstr, new_str);
            MEM_free(new_str);
        }
        wj_vstr_append_string(&vstr, ")");
        break;
	case WJ_ASSOC_VALUE:
	case WJ_CLOSURE_VALUE:
	case WJ_FAKE_METHOD_VALUE:
	case WJ_SCOPE_CHAIN_VALUE:
    default:
        DBG_panic(("value->type..%d\n", value->type));
    }
    return vstr.string;
}
